﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using ComponentBuilder;
using ComponentBuilder.Forms;

namespace BlamanticUI.Components.Forms;

/// <summary>
/// Represents an input text with a calendar witch supports <see cref="DateTime"/> type only.
/// </summary>
public class TextDate : BlazorInputComponentBase<DateTime?>, IHasOnActive,IHasInverted
{
    /// <summary>
    /// Initializes a new instance of <see cref="TextDate"/> class.
    /// </summary>
    public TextDate()
    {
        //var supportTypes = new[] { typeof(DateTime), typeof(DateTimeOffset), typeof(DateTime?), typeof(DateTimeOffset?) };
        //if (!supportTypes.Contains(typeof(TValue)))
        //{
        //    throw new ArgumentException("Only support DateTime or DateTimeOffset type");
        //}
    }

    /// <summary>
    /// Gets or sets the format of value displayed in input.
    /// </summary>
    [Parameter] public string Format { get; set; }

    /// <summary>
    /// Gets or sets a value indicating whether calendar is displayed.
    /// </summary>
    [Parameter] public bool Active { get; set; }
    /// <summary>
    /// Gets or sets the a callback method whether active state has changed.
    /// </summary>
    [Parameter] public EventCallback<bool> OnActive { get; set; }
    /// <summary>
    /// Gets or sets a value indicating whether this <see cref="TextDate"/> is disabled.
    /// </summary>
    /// <value>
    ///   <c>true</c> if disabled; otherwise, <c>false</c>.
    /// </value>
    [Parameter] public bool Disabled { get; set; }

    /// <summary>
    /// Gets or sets the view mode of calendar.
    /// </summary>
    [Parameter] public CalendarViewMode ViewMode { get; set; } = CalendarViewMode.Date;
    [Parameter]public bool Inverted { get; set; }
    /// <summary>
    /// The width of calendar. Default is 400;
    /// </summary>
    [Parameter] public int Width { get; set; } = 400;
    /// <summary>
    /// The color of calendar.
    /// </summary>
    [Parameter]public Color? Color { get; set; }

    /// <summary>
    /// Method invoked when the component has received parameters from its parent in
    /// the render tree, and the incoming values have been assigned to properties.
    /// </summary>
    protected override void OnParametersSet()
    {
        if (string.IsNullOrWhiteSpace(Format))
        {
            switch (ViewMode)
            {
                case CalendarViewMode.Year:
                    Format = "yyyy";
                    break;
                case CalendarViewMode.Month:
                    Format = "yyyy/MM";
                    break;
                case CalendarViewMode.Date:
                    Format = "yyyy/MM/dd";
                    break;
                default:
                    Format = "yyyy/MM/dd HH:mm:ss";
                    break;
            }
        }
    }

    /// <summary>
    /// Renders the component to the supplied <see cref="T:Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder" />.
    /// </summary>
    /// <param name="builder">A <see cref="T:Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder" /> that will receive the render output.</param>
    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        builder.OpenComponent<InputBox>(0);

        if (!Disabled)
        {
            builder.AddAttribute(1, "onfocusin", HtmlHelper.CreateCallback<FocusEventArgs>(this, ShowCalendar));
            builder.AddAttribute(2, "onfocusout", HtmlHelper.CreateCallback<FocusEventArgs>(this, HideCalendar));
            builder.AddAttribute(3, "tabindex", 1);
        }
        builder.AddAttribute(4, nameof(InputBox.Icon), HorizontalPosition.Left);
        builder.AddChildContentAttribute(5, BuildInput);
        builder.CloseComponent();
    }

    /// <summary>
    /// Builds the input.
    /// </summary>
    /// <param name="builder">The builder.</param>
    void BuildInput(RenderTreeBuilder builder)
    {
        builder.CreateComponent<Icon>(0, attributes: new { IconClass = "calendar" });

        builder.CreateElement(1, "input", attributes: new
        {
            Disabled,
            name = FieldIdentifier.FieldName,
            value = CurrentValueAsString,
            onchange = HtmlHelper.CreateCallback<ChangeEventArgs>(this, InputText)
        });


        builder.CreateComponent<Calendar>(2, attributes: new
        {
            AdditionalStyle = HtmlHelper.CreateStyleBuilder().
                                    Append("display:block", Active)
                                    .Append("display:none", !Active)
                                    .Append("z-index:100")
                                    .Append("position:absolute")
                                    .Append("top:40px")
                                    .Append($"min-width:{Width}px")
                                    .ToString(),
            ViewMode,
            Inverted,
            Color,
            Value,
            ValueExpression,
            ValueChanged=HtmlHelper.CreateCallback<DateTime?>(this, async _value =>
            {
                CurrentValue = _value;
                await HideCalendar(new FocusEventArgs());
            })
        });
    }

    /// <summary>
    /// Shows the calendar.
    /// </summary>
    /// <param name="e">The <see cref="FocusEventArgs"/> instance containing the event data.</param>
    async Task ShowCalendar(FocusEventArgs e)
    {
        await this.Activate();
    }

    /// <summary>
    /// Hides the calendar.
    /// </summary>
    /// <param name="e">The <see cref="FocusEventArgs"/> instance containing the event data.</param>
    async Task HideCalendar(FocusEventArgs e)
    {
        await this.Activate(false);
    }

    /// <summary>
    /// Inputs the text.
    /// </summary>
    /// <param name="e">The <see cref="ChangeEventArgs"/> instance containing the event data.</param>
    void InputText(ChangeEventArgs e)
    {
        FormatValue(e.Value);
    }

    /// <summary>
    /// Formats the value.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    protected virtual string FormatValue(object value)
        => value switch
        {
            DateTime => DateTime.Parse(value.ToString()).ToString(Format),
            DateTimeOffset => DateTimeOffset.Parse(value.ToString()).ToString(Format),
            _ => string.Empty
        };

    /// <summary>
    /// Formats the value as a string. Derived classes can override this to determine the formating used for <see cref="P:BlamanticUI.FormInputBase`1.CurrentValueAsString" />.
    /// </summary>
    /// <param name="value">The value to format.</param>
    /// <returns>
    /// A string representation of the value.
    /// </returns>
    protected override string FormatValueAsString(DateTime? value) => FormatValue(value);


}
